package org.lc.cmd.impl.beans;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.lc.cmd.Json;
import org.lc.cmd.exp.DevException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 使用SpringWeb的json解析器
 */
@Slf4j
public class SpringJsonUtil implements Json {
    public static SpringJsonUtil INSTANCE = null;

    private final List<HttpMessageConverter<?>> jsonConverters;

    /**
     * 由其他模块以bean的方式实例化此类，考虑到可能有多个实现，因此使用list注入
     */
    public SpringJsonUtil(List<HttpMessageConverter<?>> jsonConverters) {
        this.jsonConverters = jsonConverters;
        INSTANCE = this;
    }

    @Override
    public <T> T parse(String json, Class<T> clazz) {
        try {
            return new StringInput(json).read(clazz, jsonConverters);
        } catch (IOException e) {
            throw new DevException(e);
        }
    }

    @Override
    public String toJson(Object obj) {
        try {
            return new StringOutput().write(obj, jsonConverters);
        } catch (IOException e) {
            throw new DevException(e);
        }
    }

    @RequiredArgsConstructor
    private static class StringInput implements HttpInputMessage {
        private final String string;

        @Override
        public InputStream getBody() {
            return new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
        }

        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            return httpHeaders;
        }

        @SuppressWarnings("unchecked")
        public <T> T read(Class<T> clazz, List<HttpMessageConverter<?>> jsonConverters) throws IOException {
            for (HttpMessageConverter<?> jsonConverter : jsonConverters) {
                if (jsonConverter.canRead(clazz, MediaType.APPLICATION_JSON)) {
                    return ((HttpMessageConverter<T>)jsonConverter).read(clazz, this);
                }
            }
            log.warn("failed to parse json: {}", string);
            return null;
        }
    }

    private static class StringOutput implements HttpOutputMessage {
        private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();


        @Override
        public OutputStream getBody() throws IOException {
            return outputStream;
        }

        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            return httpHeaders;
        }

        @Override
        public String toString() {
            try {
                return outputStream.toString(StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException e) {
                throw new DevException(e);
            }
        }

        @SuppressWarnings("unchecked")
        public String write(Object obj, List<HttpMessageConverter<?>> jsonConverters) throws IOException {
            for (HttpMessageConverter<?> jsonConverter : jsonConverters) {
                if (jsonConverter.canWrite(obj.getClass(), MediaType.APPLICATION_JSON)) {
                    ((HttpMessageConverter<Object>)jsonConverter).write(obj, MediaType.APPLICATION_JSON, this);
                    return toString();
                }
            }
            log.warn("failed to convert to json: {}", obj);
            return null;
        }
    }
}
